﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace Puppy.Scheduler.Processors
{
    /// <summary>
    /// 执行器基类
    /// </summary>
    public abstract class ProcessorBase : IProcessor
    {
        public IServiceProvider ServiceProvider { get; set; }

        protected readonly object ServiceProviderLock = new object();

        protected TService LazyGetRequiredService<TService>(ref TService reference)
            => LazyGetRequiredService(typeof(TService), ref reference);

        protected TRef LazyGetRequiredService<TRef>(Type serviceType, ref TRef reference)
        {
            if (reference == null)
            {
                lock (ServiceProviderLock)
                {
                    if (reference == null)
                    {
                        reference = (TRef)ServiceProvider.GetRequiredService(serviceType);
                    }
                }
            }

            return reference;
        }

        protected ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory);
        private ILoggerFactory _loggerFactory;

        protected ILogger Logger => _lazyLogger.Value;
        private Lazy<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true);

        public async Task<ProcessResult> ProcessAsync(TaskContext taskContext, CancellationToken cancellationToken = default)
        {
            string status = "unknown";

            Logger.LogInformation("TaskContext is: {@TaskContext}", taskContext);

            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            try
            {
                var result = await ExecuteAsync(taskContext);

                status = result.IsSuccess ? "succeed" : "failed";

                Logger.LogInformation("Execute Succeed, Using {Elapsed}, ProcessResult: {ProcessResult}", stopwatch.Elapsed, result);

                return result;
            }
            catch (Exception ex)
            {
                status = "exception";
                Logger.LogError(ex, "Execute Failed!");
                return new ProcessResult(false, ex.Message);
            }
            finally
            {
                stopwatch.Stop();
                Logger.LogInformation("Status: {Status}, Cost: {Elapsed}", status, stopwatch.Elapsed);
            }
        }

        /// <summary>
        /// 任务处理
        /// </summary>
        /// <param name="context">任务上下文</param>
        /// <param name="cancellationToken">用于取消异步操作的可选令牌</param>
        /// <returns>处理结果，Result有长度限制，超长会被裁剪，不允许返回空值</returns>
        protected abstract Task<ProcessResult> ExecuteAsync(TaskContext context, CancellationToken cancellationToken = default);
    }
}
